package com.lyn.solitude.pluginhost.binder

import android.annotation.SuppressLint
import android.app.Application
import android.content.ComponentName
import android.content.Intent
import android.os.Process
import android.text.TextUtils
import android.util.Log
import com.alibaba.fastjson.JSON
import com.lyn.library.file.ConvenientFolder
import com.lyn.library.file.ConvenientFolderFactory
import com.lyn.solitude.api.PluginHostAidl
import com.lyn.solitude.api.PluginListenerAidl
import com.lyn.solitude.entity.Plugin
import com.lyn.solitude.pluginhost.activity.PluginProxyActivity
import com.lyn.solitude.pluginhost.crash.PluginCrash
import com.lyn.solitude.pluginhost.database.SqliteManager
import com.lyn.solitude.pluginhost.factory.ClassLoaderFactory
import com.lyn.solitude.pluginhost.factory.ResourcesFactory
import com.lyn.solitude.pluginhost.stack.PluginStack
import com.lyn.solitude.pluginhost.uitls.BitmapUtil
import com.lyn.solitude.pluginhost.uitls.PluginUtil
import java.io.File
import java.io.FileInputStream
import java.util.UUID
import java.util.stream.Collectors

/**
 *
 * @author longyn 2024/05/23
 * @version 1.0.0
 */
object PluginHostBinder : PluginHostAidl.Stub() {

    private const val TAG = "PluginHostBinder"
    const val PLUGIN_FOLDER = "plugin"
    const val PLUGIN_SO_FOLDER = "plugin_so"
    const val PLUGIN_OPTIMIZED = "plugin_optimized"
    const val PLUGIN_DEBUG = "plugin_debug"
    lateinit var application: Application
    lateinit var pluginFolder: ConvenientFolder
    lateinit var pluginSoFolder: ConvenientFolder
    lateinit var pluginOptimizedFolder: ConvenientFolder
    lateinit var pluginDebugFolder: ConvenientFolder

    private var pluginListenerAidl: PluginListenerAidl? = null

    fun initialize(application: Application) {
        if (this::application.isInitialized) {
            Log.d(TAG, "已经初始化过了")
            return
        }
        PluginCrash.initialize()
        Log.d(TAG, "initialize")
        this.application = application
        SqliteManager.initialize(application)

        pluginFolder = ConvenientFolderFactory.create(
            PLUGIN_FOLDER, PluginHostBinder.application.filesDir.absolutePath + "/plugin"
        )
        Log.d(TAG, "初始化插件文件夹:" + pluginFolder.getAbsolutePath())
        pluginSoFolder = ConvenientFolderFactory.create(
            PLUGIN_SO_FOLDER, PluginHostBinder.application.filesDir.absolutePath + "/plugin_so"
        )
        Log.d(TAG, "初始化插件SO文件夹:" + pluginSoFolder.getAbsolutePath())
        pluginOptimizedFolder = ConvenientFolderFactory.create(
            PLUGIN_OPTIMIZED,
            PluginHostBinder.application.filesDir.absolutePath + "/plugin_optimized"
        )
        Log.d(TAG, "初始化插件优化文件夹:" + pluginOptimizedFolder.getAbsolutePath())
        pluginDebugFolder = ConvenientFolderFactory.create(
            PLUGIN_DEBUG,
            PluginHostBinder.application.filesDir.absolutePath + "/plugin_debug"
        )
        Log.d(TAG, "初始化插件debug文件夹:" + pluginDebugFolder.getAbsolutePath())
        Thread {
            pluginFolder.getFiles()?.forEach { pluginFolderFile ->
                pluginFolderFile?.let {
                    if (pluginFolderFile.isDirectory) {
                        pluginFolder.deleteFile(pluginFolderFile.name)
                    }
                }
            }
            val plugins = SqliteManager.findAll()
            Log.d(TAG, "插件数据:" + JSON.toJSONString(plugins))
            if (plugins.isEmpty()) {
                pluginFolder.deleteAllFile()
                pluginFolder.getFiles()?.forEach { pluginFolderFile ->
                    pluginFolderFile?.let { pluginFolder.deleteFolderFile(it.absolutePath) }
                }
                pluginSoFolder.deleteAllFile()
                pluginSoFolder.getFiles()?.forEach { pluginSoFolderFile ->
                    pluginSoFolderFile?.let { pluginSoFolder.deleteFolderFile(it.absolutePath) }
                }
            }
            val pluginFiles = pluginFolder.getFiles()
            if (pluginFiles != null) {
                for (pluginFile in pluginFiles) {
                    if (pluginFile == null) {
                        continue
                    }
                    val collect = plugins.stream()
                        .filter { plugin -> plugin.filePath == pluginFile.absolutePath }
                        .collect(Collectors.toList())
                    if (collect.isEmpty()) {
                        pluginFolder.deleteFile(pluginFile.name)
                        Log.d(TAG, "处理掉无用插件文件:" + pluginFile.name)
                    }
                }
            }

            val pluginSoFolderFiles = pluginSoFolder.getFiles()
            if (pluginSoFolderFiles != null) {
                for (pluginSoFolderFile in pluginSoFolderFiles) {
                    if (pluginSoFolderFile == null) {
                        continue
                    }
                    val collect = plugins.stream()
                        .filter { plugin -> plugin.soFolderPath == pluginSoFolderFile.absolutePath }
                        .collect(Collectors.toList())
                    Log.d(TAG, "collect:" + JSON.toJSONString(collect))
                    if (collect.isEmpty()) {
                        pluginSoFolder.deleteFolderFile(pluginSoFolderFile.absolutePath)
                        Log.d(TAG, "处理掉无用插件SO文件夹:" + pluginSoFolderFile.name)
                    }
                }
            }

        }.start()
    }

    override fun getPid(): Int {
        return Process.myPid()
    }


    @SuppressLint("UseCompatLoadingForDrawables")
    override fun install(path: String): Boolean {
        val file = File(path)
        if (!file.exists()) {
            Log.d(TAG, "安装路径文件不存在")
            pluginListenerAidl?.onInstall(false, path, "安装路径文件不存在", Process.myPid())
            return false
        }
        if (file.isDirectory) {
            Log.d(TAG, "是文件夹")
            pluginListenerAidl?.onInstall(false, path, "是文件夹", Process.myPid())
            return false
        }
        val writeFilePath =
            pluginFolder.writeFile(
                FileInputStream(path),
                UUID.randomUUID().toString().replace("-", "") + ".apk"
            )
        if (writeFilePath == null || TextUtils.isEmpty(writeFilePath)) {
            pluginListenerAidl?.onInstall(false, path, "保存插件失败", Process.myPid())
            return false
        }
        val pluginFile = File(writeFilePath)
        pluginFile.setReadOnly()
        val pluginFilePath = pluginFile.absolutePath
        val pluginFileName = pluginFile.name
        val pluginUUID = PluginUtil.getPluginUUID(pluginFilePath, application)
        if (TextUtils.isEmpty(pluginUUID)) {
            Log.d(TAG, "插件UUID获取失败")
            pluginFolder.deleteFile(pluginFileName)
            pluginListenerAidl?.onInstall(false, path, "插件UUID获取失败", Process.myPid())
            return false
        }
        var oldPlugin: Plugin? = null
        SqliteManager.findByUUidFirst(pluginUUID)?.let {
            oldPlugin = it
            val oldPluginFileMd5 = PluginUtil.getPluginFileMd5(File(it.filePath))
            if (!TextUtils.isEmpty(oldPluginFileMd5)) {
                if (oldPluginFileMd5 == PluginUtil.getPluginFileMd5(pluginFile)) {
                    Log.d(TAG, "插件已经安装过:$path")
                    pluginFolder.deleteFile(pluginFileName)
                    pluginListenerAidl?.onInstall(true, path, "插件已经安装过", Process.myPid())
                    return false
                }
            }
        }
        val pluginVersion = PluginUtil.getPluginVersion(pluginFilePath, application)
        val pluginMainClassName = PluginUtil.getPluginMainClassName(pluginFilePath, application)
        if (TextUtils.isEmpty(pluginMainClassName)) {
            Log.d(TAG, "插件类名获取失败")
            pluginListenerAidl?.onInstall(false, path, "插件类名获取失败", Process.myPid())
            pluginFolder.deleteFile(pluginFileName)
            return false
        }
        val pluginNameResId = PluginUtil.getPluginNameResId(pluginFilePath, application)
        if (pluginNameResId == -1) {
            Log.d(TAG, "插件名称resId获取失败")
            pluginListenerAidl?.onInstall(false, path, "插件名称resId获取失败", Process.myPid())
            pluginFolder.deleteFile(pluginFileName)
            return false
        }
        val pluginLogoResId = PluginUtil.getPluginLogoResId(pluginFilePath, application)
        if (pluginLogoResId == -1 || pluginLogoResId == 0xff) {
            Log.d(TAG, "插件logo resId 获取失败")
            pluginListenerAidl?.onInstall(false, path, "插件logo resId 获取失败", Process.myPid())
            pluginFolder.deleteFile(pluginFileName)
            return false
        }
        val resources = ResourcesFactory.get(pluginFilePath) ?: return false
        val name = resources.getString(pluginNameResId)
        if (TextUtils.isEmpty(name)) {
            Log.d(TAG, "插件名称获取失败")
            pluginListenerAidl?.onInstall(false, path, "插件名称获取失败", Process.myPid())
            ResourcesFactory.remove(pluginFilePath)
            pluginFolder.deleteFile(pluginFileName)
            return false
        }
        try {
            resources.getDrawable(pluginLogoResId, null)
        } catch (e: Throwable) {
            e.printStackTrace()
            Log.d(TAG, "插件logo获取失败")
            pluginListenerAidl?.onInstall(false, path, "插件logo获取失败", Process.myPid())
            ResourcesFactory.remove(pluginFilePath)
            pluginFolder.deleteFile(pluginFileName)
            return false
        }
        val classLoader = ClassLoaderFactory.get(pluginFilePath)
        if (classLoader == null) {
            Log.d(TAG, "插件类加载器获取失败")
            pluginListenerAidl?.onInstall(false, path, "插件类加载器获取失败", Process.myPid())
            ResourcesFactory.remove(pluginFilePath)
            pluginFolder.deleteFile(pluginFileName)
            return false
        }
        oldPlugin?.apply {
            uuid = pluginUUID
            this.name = name
            version = pluginVersion
            val oldFilePath:String = filePath
            filePath = pluginFilePath
            val oldSoFolderPath:String = soFolderPath
            soFolderPath = ClassLoaderFactory.getSoPath(pluginFilePath)
            mainClassName = pluginMainClassName
            time = System.currentTimeMillis()
            val update = SqliteManager.update(this)
            if (update) {
                pluginFolder.deleteFolderFile(oldFilePath)
                pluginSoFolder.deleteFolderFile(oldSoFolderPath)
                pluginListenerAidl?.onInstall(true, path, "插件更新成功", Process.myPid())
                return true
            }
            ResourcesFactory.remove(pluginFilePath)
            pluginFolder.deleteFile(pluginFileName)
            ClassLoaderFactory.remove(pluginFilePath)
            Log.d(TAG, "update != 1")
            pluginListenerAidl?.onInstall(false, path, "保存到数据库失败", Process.myPid())
            return false
        }
        val plugin = Plugin().apply {
            uuid = pluginUUID
            this.name = name
            version = pluginVersion
            filePath = pluginFilePath
            soFolderPath = ClassLoaderFactory.getSoPath(pluginFilePath)
            mainClassName = pluginMainClassName
            time = System.currentTimeMillis()
        }
        if (!SqliteManager.save(plugin)) {
            ResourcesFactory.remove(pluginFilePath)
            pluginFolder.deleteFile(pluginFileName)
            ClassLoaderFactory.remove(pluginFilePath)
        }
        pluginListenerAidl?.onInstall(true, path, "插件安装成功", Process.myPid())
        return true
    }

    override fun unInstall(id: Long): Boolean {
        SqliteManager.findByIdFirst(id)?.let { plugin ->
            File(plugin.filePath).delete()
            File(plugin.soFolderPath).delete()
            ResourcesFactory.remove(plugin.filePath)
            ClassLoaderFactory.remove(plugin.filePath)
            val delete = SqliteManager.delete(id)
            if (delete) {
                pluginListenerAidl?.onUnInstall(
                    true,
                    id,
                    plugin.filePath,
                    "卸载成功",
                    Process.myPid()
                )
            } else {
                pluginListenerAidl?.onUnInstall(
                    false,
                    id,
                    plugin.filePath,
                    "卸载失败",
                    Process.myPid()
                )
            }
            return delete
        }
        return false
    }

    override fun getPlugin(id: Long): ByteArray? {
        SqliteManager.findByIdFirst(id)?.let { plugin ->
            Plugin.toByteArray(plugin)?.let {
                return it
            }
        }
        return null
    }

    override fun getAllPlugin(): ByteArray? {
        val plugins = SqliteManager.findAll()
        plugins.forEach { plugin ->
            ResourcesFactory.get(plugin.filePath)
            ClassLoaderFactory.get(plugin.filePath)
        }
        return Plugin.toByteArray(plugins)
    }

    @SuppressLint("UseCompatLoadingForDrawables")
    override fun getLogo(id: Long): ByteArray? {
        try {
            SqliteManager.findByIdFirst(id)?.let { plugin ->
                val pluginLogoResId = PluginUtil.getPluginLogoResId(plugin.filePath, application)
                ResourcesFactory.get(plugin.filePath)?.let { resources ->
                    val drawable = resources.getDrawable(pluginLogoResId, null)
                    val drawableToBitmap = BitmapUtil.drawableToBitmap(drawable)
                    return BitmapUtil.bitmapToByteArray(drawableToBitmap)
                }
            }
        }catch (e:Throwable){
            e.printStackTrace()
        }
        return null
    }

    override fun open(id: Long): Boolean {
        SqliteManager.findByIdFirst(id)?.let { plugin ->
            if (TextUtils.isEmpty(plugin.mainClassName)) {
                pluginListenerAidl?.onOpen(
                    false, id, plugin.filePath, "插件 mainClassName 获取失败", Process.myPid()
                )
                return false
            }
            PluginStack.push(plugin)
            val intent = Intent()
            intent.putExtra(
                PluginProxyActivity.PLUGIN_ACTIVITY_CLASS_NAME_KEY,
                plugin.mainClassName
            )
            val componentName = ComponentName(
                application.packageName,
                PluginProxyActivity::class.java.name
            )
            intent.setComponent(componentName)
            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
            application.startActivity(intent)
            pluginListenerAidl?.onOpen(
                true, id, plugin.filePath, "打开插件", Process.myPid()
            )
            return true
        }
        pluginListenerAidl?.onOpen(
            false, id, "", "插件未找到", Process.myPid()
        )
        return false
    }

    override fun jump(id: Long, className: String?): Boolean {
        SqliteManager.findByIdFirst(id)?.let { plugin ->
            PluginStack.push(plugin)
            val intent = Intent()
            intent.putExtra(
                PluginProxyActivity.PLUGIN_ACTIVITY_CLASS_NAME_KEY,
                className
            )
            val componentName = ComponentName(
                application.packageName,
                PluginProxyActivity::class.java.name
            )
            intent.setComponent(componentName)
            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
            application.startActivity(intent)
            pluginListenerAidl?.onJump(
                true, id, plugin.filePath, "跳转插件页面:$className", Process.myPid()
            )
            return true
        }
        return false
    }

    override fun setPluginListenerAidl(listener: PluginListenerAidl) {
        this.pluginListenerAidl = listener
        this.pluginListenerAidl?.onListenerInitialize(Process.myPid())
    }


}